#include "lib_modbus.h"

#ifndef MB_PORT_HAS_CLOSE
#define MB_PORT_HAS_CLOSE 0
#endif

#include "../inc_m/mbfuncother.c"

/* ----------------------- Static variables ---------------------------------*/
static UCHAR    ucMBAddress;		//	адресс устройства.
static eMBMode  eMBCurrentMode;		//	режим работы RTU ASCII

static USHORT   usRegHoldingStart = REG_HOLDING_START;
static USHORT   usRegHoldingBuf[REG_HOLDING_NREGS];

static enum
{
    STATE_ENABLED,
    STATE_DISABLED,
    STATE_NOT_INITIALIZED
} eMBState = STATE_NOT_INITIALIZED;

/* Callback functions required by the porting layer. They are called when
 * an external event has happend which includes a timeout or the reception
 * or transmission of a character.
 */
//BOOL( *pxMBFrameCBByteReceived ) ( void );
//BOOL( *pxMBFrameCBTransmitterEmpty ) ( void );
//BOOL( *pxMBPortCBTimerExpired ) ( void );

BOOL( *pxMBFrameCBReceiveFSMCur ) ( void );
BOOL( *pxMBFrameCBTransmitFSMCur ) ( void );

/* An array of Modbus functions handlers which associates Modbus function
 * codes with implementing functions.
 */
static xMBFunctionHandler xFuncHandlers[MB_FUNC_HANDLERS_MAX] = {
#if MB_FUNC_READ_HOLDING_ENABLED > 0									//	F3
    {MB_FUNC_READ_HOLDING_REGISTER, eMBFuncReadHoldingRegister},
#endif
#if MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED > 0							//	F16
    {MB_FUNC_WRITE_MULTIPLE_REGISTERS, eMBFuncWriteMultipleHoldingRegister},
#endif
#if MB_FUNC_WRITE_HOLDING_ENABLED > 0									//	F6
    {MB_FUNC_WRITE_REGISTER, eMBFuncWriteHoldingRegister},
#endif
};



/* ----------------------- Start implementation -----------------------------*/
eMBErrorCode eMBInit ( eMBMode eMode, UCHAR ucSlaveAddress, ULONG ulBaudRate, eMBParity eParity ) {
	eMBErrorCode	eStatus = MB_ENOERR;

    /* check preconditions */
    if( ( ucSlaveAddress == MB_ADDRESS_BROADCAST ) || ( ucSlaveAddress < MB_ADDRESS_MIN ) || ( ucSlaveAddress > MB_ADDRESS_MAX ) ) {
    	eStatus = MB_EINVAL;
    } else {
    	ucMBAddress = ucSlaveAddress;
    	switch ( eMode ) {
    	case MB_RTU:
            //pxMBFrameCBByteReceived = xMBRTUReceiveFSM;
            //pxMBFrameCBTransmitterEmpty = xMBRTUTransmitFSM;
            //pxMBPortCBTimerExpired = xMBRTUTimerT35Expired;
            eStatus = eMBRTUInit(ulBaudRate, eParity );
            break;
        default:
            eStatus = MB_EINVAL;
        }

        if( eStatus == MB_ENOERR ) {
        	EventInit();
            //if( !xMBPortEventInit() ) {
            //    /* port dependent event module initalization failed. */
            //    eStatus = MB_EPORTERR;
            //}
            //else
            //{
                eMBCurrentMode = eMode;
                eMBState = STATE_DISABLED;
            //}
        }
    }
    return eStatus;
}
//
//{
    //int             i;
    //eMBErrorCode    eStatus;

    //if( ( 0 < ucFunctionCode ) && ( ucFunctionCode <= 127 ) )
//    {
        //ENTER_CRITICAL_SECTION(  );
//        if( pxHandler != NULL )
        //{
//            for( i = 0; i < MB_FUNC_HANDLERS_MAX; i++ )
//            {
//                if( ( xFuncHandlers[i].pxHandler == NULL ) ||
//                    ( xFuncHandlers[i].pxHandler == pxHandler ) )
//                {
//                    xFuncHandlers[i].ucFunctionCode = ucFunctionCode;
//                    xFuncHandlers[i].pxHandler = pxHandler;
//                    break;
//                }
//            }
//            eStatus = ( i != MB_FUNC_HANDLERS_MAX ) ? MB_ENOERR : MB_ENORES;
//        }
//        else
//        {
//            for( i = 0; i < MB_FUNC_HANDLERS_MAX; i++ )
//            {
//                if( xFuncHandlers[i].ucFunctionCode == ucFunctionCode )
//                {
//                    xFuncHandlers[i].ucFunctionCode = 0;
//                    xFuncHandlers[i].pxHandler = NULL;
//                    break;
//                }
            //}
//            /* Remove can't fail. */
//            eStatus = MB_ENOERR;
//        }
//        EXIT_CRITICAL_SECTION(  );
//    }
//    else
//    {
//        eStatus = MB_EINVAL;
//    }
//    return eStatus;
//}



extern USHORT   usRegHoldingStart;
extern USHORT   usRegHoldingBuf[];

eMBErrorCode eMBRegHoldingCB( UCHAR *pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode ) {
    eMBErrorCode    eStatus = MB_ENOERR;
    int             iRegIndex;

    if( ( usAddress >= REG_HOLDING_START ) && ( usAddress + usNRegs <= REG_HOLDING_START + REG_HOLDING_NREGS ) ) {
        iRegIndex = ( int )( usAddress - usRegHoldingStart );
        switch ( eMode ) {
            /* Pass current register values to the protocol stack. */
        case MB_REG_READ:
            while( usNRegs > 0 ) {
                *pucRegBuffer++ = ( UCHAR ) ( usRegHoldingBuf[iRegIndex] >> 8 );
                *pucRegBuffer++ = ( UCHAR ) ( usRegHoldingBuf[iRegIndex] & 0xFF );
                iRegIndex++;
                usNRegs--;
            }
            break;
            /* Update current register values with new values from the
             * protocol stack. */
        case MB_REG_WRITE:
            while( usNRegs > 0 ) {
                usRegHoldingBuf[iRegIndex] = *pucRegBuffer++ << 8;
                usRegHoldingBuf[iRegIndex] |= *pucRegBuffer++;
                iRegIndex++;
                usNRegs--;
            }
        }
    } else {
        eStatus = MB_ENOREG;
    }
    return eStatus;
}

eMBErrorCode eMBEnable (void) {
	eMBErrorCode	eStatus = MB_ENOERR;
    if( eMBState == STATE_DISABLED ) {
        /* Activate the protocol stack. */
    	eMBRTUStart();
        eMBState = STATE_ENABLED;
    } else {
        eStatus = MB_EILLSTATE;
    }
    return eStatus;
}

eMBErrorCode eMBDisable( void ) {
    eMBErrorCode    eStatus;
    if( eMBState == STATE_ENABLED ) {
    	eMBRTUStop();
        eMBState = STATE_DISABLED;
        eStatus = MB_ENOERR;
    } else if( eMBState == STATE_DISABLED ) {
        eStatus = MB_ENOERR;
    } else {
        eStatus = MB_EILLSTATE;
    }
    return eStatus;
}


eMBErrorCode eMBPoll( void )
{
    static UCHAR   *ucMBFrame;
    static UCHAR    ucRcvAddress;
    static UCHAR    ucFunctionCode;
    static USHORT   usLength;
    static eMBException eException;

    int             i;
    eMBErrorCode    eStatus = MB_ENOERR;
    eMBEventType    eEvent;

    /* Check if the protocol stack is ready. */
    if( eMBState != STATE_ENABLED ) {
        return MB_EILLSTATE;
    }

    /* Check if there is a event available. If not return control to caller.
     * Otherwise we will handle the event. */
    if( EventGet( &eEvent ) == TRUE ) {
        switch ( eEvent )
        {
        case EV_READY:
            break;

        case EV_FRAME_RECEIVED:
            eStatus = eMBRTUReceive( &ucRcvAddress, &ucMBFrame, &usLength );
            if( eStatus == MB_ENOERR )
            {
                /* Check if the frame is for us. If not ignore the frame. */
                if( ( ucRcvAddress == ucMBAddress ) || ( ucRcvAddress == MB_ADDRESS_BROADCAST ) )
                {
                    EventPost( EV_EXECUTE );
                }
            }
            break;

        case EV_EXECUTE:
            ucFunctionCode = ucMBFrame[MB_PDU_FUNC_OFF];
            eException = MB_EX_ILLEGAL_FUNCTION;
            for( i = 0; i < MB_FUNC_HANDLERS_MAX; i++ )
            {
                /* No more function handlers registered. Abort. */
                if( xFuncHandlers[i].ucFunctionCode == 0 )
                {
                    break;
                }
                else if( xFuncHandlers[i].ucFunctionCode == ucFunctionCode )
                {
                    eException = xFuncHandlers[i].pxHandler( ucMBFrame, &usLength );
                    break;
                }
            }


            if( ucRcvAddress != MB_ADDRESS_BROADCAST ) {							// If the request was not sent to the broadcast address we return a reply.
                if( eException != MB_EX_NONE ) {
                    /* An exception occured. Build an error frame. */
                    usLength = 0;
                    ucMBFrame[usLength++] = ( UCHAR )( ucFunctionCode | MB_FUNC_ERROR );
                    ucMBFrame[usLength++] = eException;
                }
                //if( ( eMBCurrentMode == MB_ASCII ) && MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS )
                //{
                //    vMBPortTimersDelay( MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS );
                //}
                eStatus = eMBRTUSend( ucMBAddress, ucMBFrame, usLength );
            }
            break;

        case EV_FRAME_SENT:
            break;
        }
    }
    return MB_ENOERR;
}
